from models.dao import permission_dao, role_permission_dao, user_role_dao
from models.database import SessionLocal
from sqlalchemy.orm import joinedload # Import joinedload for eager loading


def getPermissionsByUser(skip: int = 0, limit: int = 100, ismenu: bool = False, user_id: str = None):
    """
    获取用户有权限的权限列表
    通过user_id关联user_roles表、roles表、role_permissions表
    只返回有role_permissions的permissions
    """
    with SessionLocal() as db:
        if not user_id:
            return []

        # 1. 获取用户的所有角色
        user_roles = user_role_dao.get_roles_by_user_id(db, user_id)
        if not user_roles:
            return []

        # 2. 获取用户所有角色对应的权限ID集合
        user_permission_ids = set()
        for user_role in user_roles:
            role_permissions = role_permission_dao.get_permissions_by_role_id(db, user_role.role_id)
            for rp in role_permissions:
                if rp.permission:
                    user_permission_ids.add(rp.permission.id)

        if not user_permission_ids:
            return []

        # 3. 获取用户有权限的权限记录（包含子权限）
        user_permissions = db.query(permission_dao.PermissionModel).options(
            joinedload(permission_dao.PermissionModel.children)
        ).filter(permission_dao.PermissionModel.id.in_(user_permission_ids)).all()

        # 4. 过滤非菜单项（如果需要）
        if ismenu:
            user_permissions = [
                p for p in user_permissions
                if p.component is not None or p.order is not None
            ]

        # 5. 识别根权限（没有父权限的）
        root_permissions = []
        for p in user_permissions:
            if p.parent_id is None or p.parent_id == "":
                root_permissions.append(p)

        # 6. 按order排序
        root_permissions.sort(key=lambda x: x.order if x.order is not None else float('inf'))

        # 7. 转换为字典格式，包含子权限
        permission_tree = [p.to_dict_tree(with_children=True) for p in root_permissions]

        return permission_tree

def getPermissions(skip: int = 0, limit: int = 100, ismenu: bool = False):
    with SessionLocal() as db:
        # Fetch all permissions with their children eagerly loaded
        all_permissions_models = db.query(permission_dao.PermissionModel).options(joinedload(permission_dao.PermissionModel.children)).all()
        
        # Filter out non-menu items (e.g., API permissions)
        menu_items_filtered=[]
        if ismenu:
            menu_items_filtered = [
            p for p in all_permissions_models
                if p.component is not None or p.order is not None
        ]
        else:
            menu_items_filtered = all_permissions_models


        # Identify root menus from the filtered list
        root_menus = []
        for p in menu_items_filtered:
            if p.parent_id is None or p.parent_id == "":
                root_menus.append(p)

        # Sort root menus by order
        root_menus.sort(key=lambda x: x.order if x.order is not None else float('inf'))

        # Convert to dictionary format, leveraging the SQLAlchemy relationship for children
        menu_tree = [p.to_dict_tree(with_children=True) for p in root_menus]
        
        return menu_tree

def getPermissionList():
    with SessionLocal() as db:
        permissionModel = permission_dao.getPermissionList(db)
        if permissionModel is None:
            return None
        # 按order字段排序，order为None的放在最后
        # sorted_permissions = sorted(permissionModel, key=lambda x: x.order if x.order is not None else float('inf'))
        return [c.to_dict() for c in permissionModel]

def getPermission(permission_id: str):
    with SessionLocal() as db:
        permissionModel = permission_dao.getPermission(db, permission_id=permission_id)
        if permissionModel is None:
            return None
        return permissionModel.to_dict()

def createPermission(permission_data: dict):
    with SessionLocal() as db:
        newPermissionModel = permission_dao.createPermission(db, permission_data)
        if newPermissionModel is None:
            return None
        return newPermissionModel.to_dict()

def updatePermission(permission_id: str, permission_data: dict):
    with SessionLocal() as db:
        updatedPermissionModel = permission_dao.updatePermission(db, permission_id=permission_id, permission=permission_data)
        if updatedPermissionModel is None:
            return None
        return updatedPermissionModel.to_dict()

def deletePermission(permission_id: str):
    with SessionLocal() as db:
        return permission_dao.deletePermission(db, permission_id=permission_id)

def get_permissions_by_role_id(role_id: str):
    with SessionLocal() as db:
        role_permissions = role_permission_dao.get_permissions_by_role_id(db, role_id)
        return [rp.permission.to_dict() for rp in role_permissions]

def menu_name_exists(name: str, menu_id: str = None):
    with SessionLocal() as db:
        permission_model = permission_dao.get_by_name(db, name=name)
        if permission_model is None:
            return False  # Name doesn't exist, so it's available
        if menu_id and str(permission_model.id) == str(menu_id):
            return False  # Name exists, but it belongs to the item being edited
        return True # Name exists and belongs to another item

def menu_path_exists(path: str, menu_id: str = None):
    with SessionLocal() as db:
        # Assuming 'endpoint' in the database corresponds to 'path' in the frontend
        permission_model = permission_dao.get_by_path(db, path=path)
        if permission_model is None:
            return False  # Path doesn't exist, so it's available
        if menu_id and str(permission_model.id) == str(menu_id):
            return False  # Path exists, but it belongs to the item being edited
        return True  # Path exists and belongs to another item

def save_menu(menu_data: dict):
    """
    创建或更新菜单数据
    如果存在id则更新，否则创建新菜单
    """
    with SessionLocal() as db:
        menu_id = menu_data.get("id")

        # 如果存在id，则更新现有菜单
        if menu_id:
            existing_menu = permission_dao.getPermission(db, menu_id)
            if existing_menu:
                return permission_dao.updatePermission(db, menu_id, menu_data).to_dict()
            else:
                # 如果id不存在，则创建新菜单（id由数据库自动生成）
                menu_data.pop("id", None)  # 移除id字段，让数据库自动生成
                new_menu = permission_dao.createPermission(db, menu_data)
                return new_menu.to_dict() if new_menu else None
        else:
            # 没有id，创建新菜单
            new_menu = permission_dao.createPermission(db, menu_data)
            return new_menu.to_dict() if new_menu else None


def delete_menu(menu_id: str):
    """
    删除菜单及其所有子菜单，同时删除对应的role_permissions记录
    返回True表示删除成功，False表示删除失败
    """
    with SessionLocal() as db:
        try:
            # 首先检查菜单是否存在
            menu = permission_dao.getPermission(db, menu_id)
            if not menu:
                return False
            
            # 递归删除所有子菜单及其role_permissions记录
            def delete_children(parent_id: str):
                children = permission_dao.getPermissions(db, parent_id=parent_id)
                for child in children:
                    delete_children(child.id)  # 递归删除子菜单的子菜单
                    
                    # 先删除该子菜单的role_permissions记录
                    role_permissions = role_permission_dao.get_permissions_by_permission_id(db, child.id)
                    for rp in role_permissions:
                        db.delete(rp)
                    
                    # 然后删除子菜单本身
                    permission_dao.deletePermission(db, child.id)
            
            # 删除所有子菜单及其role_permissions记录
            delete_children(menu_id)
            
            # 删除主菜单的role_permissions记录
            role_permissions = role_permission_dao.get_permissions_by_permission_id(db, menu_id)
            for rp in role_permissions:
                db.delete(rp)
            
            # 最后删除主菜单本身
            success = permission_dao.deletePermission(db, menu_id)
            
            # 提交事务
            db.commit()
            return success
            
        except Exception as e:
            # 发生错误时回滚事务
            db.rollback()
            return False